
.valid.spatial.query.mode <- c('identical','disjoint','intersect','touche','overlap','cross','within','contain')

PrintSpatialQueryMode <- function(){
  print(.valid.spatial.query.mode)
}

#' @export
.IsSpatialQueryModeValid <- function(name){
  if (is.null(name)) {
    stop("name must be valid")
  }
  if (!is.character(name)) {
    stop("name must be character")
  }
  if (length(.valid.spatial.query.mode[(.valid.spatial.query.mode == name)]) == 1) {
    return(TRUE)
  } else {
    warning("spatial query mode is invalid")
    return(FALSE)
  }
}

.selectGeo <- function(row) {
  return(row[[1]])
}

.selectField <- function(row){
  return(row[2:length(row)])
}


.fields2df <- function(fields){
  if (!is.null(fields) && is.list(fields) && length(fields) > 0) {
    field.names <- names(fields[[1]])
    data <- data.frame(row.names = c(1:length(fields)))
    j <- 1
    for(name in field.names){
      name.values <- sapply(fields, function(row, name){
        if(!is.null(row[[name]])){
          return(row[[name]])
        } else {
          return(NA)
        }
      }, name)
      data <- cbind(data, name.values)
      names(data)[j] <- name
      j <- j+1
    }
    return(data)
  } else {
    return(data.frame())
  }
}


.query.result.fields2df <- function(obj, result){
  if (is.null(obj)) {
    stop("obj must be valid")
  }
  if (class(obj) != "DatasetVector") {
    stop("obj must be DatasetVector")
  }
  if (!is.null(result) && is.list(result)) {
    
    fieldvalues <- lapply(result, .selectField)
    fields.df <-.fields2df(fieldvalues)
    
    if(is.null(result[[1]][[1]])) {
      return(fields.df)
    } else {
      geos <- lapply(result, .selectGeo)
      return(list(geos, fields.df))
    }
  } else {
    warning(result)
    return(NULL)
  }
}

.query2df <- function(obj, result,  has.geometry = TRUE) {
  
  if (is.null(obj)) {
    stop("obj must be valid")
  }
  if (class(obj) != "DatasetVector") {
    stop("obj must be DatasetVector")
  }
  
  if (!is.null(result) && is.list(result)) {
    if(has.geometry == FALSE) {
      return(.fields2df(lapply(result, .selectField)))
    } else {
      datasetType <- tolower(GetDatasetType(obj))
      if(datasetType == "point"){ # point
        data <- .fields2df(lapply(result, .selectField))
        pnts <- as.matrix(data[c("SMX", "SMY")])
        dimnames(pnts)[[1]] = unlist(data[c("SMID")],use.names = FALSE)
        #size <- length(result)
        #pnts <- matrix(nrow=size, ncol = 2)
        #i <- 1
        #fieldvalues <- vector(mode = "list", length = size)
        #ids <- vector(mode = "integer", length=size)
        #for(row in result){
        #  geo <- row[[1]]
        #  if(!is.null(geo)){
        #    pnt = GetPoints(geo)
        #    pnts[i,] <- pnt
        #    ids[i] <- GetID(geo)
        #  }
        #  fieldvalues[i] <- list(row[2:length(row)])
        #  i <- i+1
        #}
        #dimnames(pnts)[[1]] = ids
        #data = .fields2df(fieldvalues)
        return(sp::SpatialPointsDataFrame(pnts, data, match.ID = "SMID"))
      } else if(datasetType == "line") { #line
        if(is.null(result[[1]][[1]])){
          fieldvalues <- lapply(result, .selectField)
          return(.fields2df(fieldvalues))
        } else {
          fieldvalues <- lapply(result, .selectField)
          geos <- lapply(result, .selectGeo)
          splines <- Geometry2Sp(geos)
          data = .fields2df(fieldvalues)
          return(sp::SpatialLinesDataFrame(splines, data,  match.ID = "SMID"))
        }
      } else if(datasetType == "region") { #region
        if(is.null(result[[1]][[1]])){
          fieldvalues <- lapply(result, .selectField)
          return(.fields2df(fieldvalues))
        } else {
          fieldvalues <- lapply(result, .selectField)
          geos <- lapply(result, FUN=.selectGeo)
          spPolygons <- Geometry2Sp(geos)
          data = .fields2df(fieldvalues)
          return(sp::SpatialPolygonsDataFrame(spPolygons, data,  match.ID = "SMID"))
        }
      } else if(datasetType == "tabular") {
        return(.fields2df(lapply(result, FUN = .selectField)))
      } else {
        warning("Unsupported dataset type.")
        return(NULL)
      }
    }
  } else {
    warning(result)
    return(NULL)
  }
}

.QueryWithIDs <- function(obj, ids, id.field="SMID", has.geometry=TRUE){
  if (is.null(obj)) {
    stop("obj must be valid")
  }
  if (class(obj) != "DatasetVector") {
    stop("obj must be DatasetVector")
  }
  if (is.null(id.field)) {
    stop("id.field must be valid")
  }
  if (!.IsDatasetTypeValid(GetDatasetType(obj))) {
    stop()
  }
  qids = vector(mode="integer")
  if(!is.null(ids) && length(ids) > 0){
    qids <- as.integer(ids)
  }
  res <- .iCall("R_DatasetVector_query_ids", obj@pointer, qids, id.field, has.geometry, PACKAGE="RSuperMap")
  if(is.null(res) || is.logical(res)){
    warning("id query failed")
    return(NULL)
  } else {
    return(res)
  }
}


QueryWithIDs <- function(obj, ids, id.field="SMID", has.geometry=TRUE){
  result <- .QueryWithIDs(obj, ids, id.field, has.geometry)
 if (!is.null(result) && is.list(result)) {
   return(.query.result.fields2df(obj, result = result))
 } else{
   return(NULL)
 }
}

QueryWithIDs2DF <- function(obj, ids, id.field="SMID",  has.geometry=TRUE){
  
  if (is.null(obj)) {
    stop("obj must be valid")
  }
  if (class(obj) != "DatasetVector") {
    stop("obj must be DatasetVector")
  }
  if (is.null(id.field)) {
    stop("id.field must be valid")
  }
  dt.type <- GetDatasetType(obj)
  if (!.IsDatasetTypeValid(dt.type)) {
    stop()
  }
  
  src.has.geometry <- has.geometry
  if(dt.type == "point"){
    has.geometry = FALSE
  }
  
  result <- .QueryWithIDs(obj, ids, id.field, has.geometry)
  if (!is.null(result) && is.list(result)) {
    result.df <- .query2df(obj, result, has.geometry = src.has.geometry)
    return(result.df)
  } else {
    return(NULL)
  }
}

.Query <- function(obj, filter = "", groupby = "", has.geometry = TRUE, order = "", fields = vector(mode="character", length=0L)){
  if (is.null(obj)) {
    stop("obj must be valid")
  }
  if (class(obj) != "DatasetVector") {
    stop("obj must be DatasetVector")
  }
  if (!.IsDatasetTypeValid(GetDatasetType(obj))) {
    stop()
  }
  res <- .iCall("R_DatasetVector_query", obj@pointer, filter, groupby, has.geometry, order, fields, PACKAGE="RSuperMap")
  if(is.null(res) || is.logical(res)){
    warning("query failed")
    return(NULL)
  } else {
    return(res)
  }
}

Query <- function(obj, filter = "", groupby = "", has.geometry = TRUE, order = "", fields = vector(mode="character", length=0L)){
  result <- .Query(obj, filter, groupby, has.geometry, order, fields)
  if (!is.null(result) && is.list(result)) {
    return(.query.result.fields2df(obj, result = result))
  } else{
    return(NULL)
  }
}

Query2DF <- function(obj, filter = "", groupby = "", has.geometry = TRUE, order = "", fields = vector(mode="character", length=0L)){

  if (is.null(obj)) {
    stop("obj must be valid")
  }
  if (class(obj) != "DatasetVector") {
    stop("obj must be DatasetVector")
  }
  dt.type <- GetDatasetType(obj)
  if (!.IsDatasetTypeValid(dt.type)) {
    stop()
  }
  
  add.smx <- FALSE
  add.smy <- FALSE
  src.has.geometry <- has.geometry
  if(dt.type == "point" && has.geometry){
    if(!is.null(fields) && length(fields) > 0){
      tempFields <- toupper(fields)
      if(length(tempFields[(tempFields == "SMX")]) == 0){
        fields <- c(fields, "SMX")
        add.smx <- TRUE
      }
      if(length(tempFields[(tempFields == "SMY")]) == 0){
        fields <- c(fields, "SMY")
        add.smy <- TRUE
      }
    }
  }

  result <- .Query(obj, filter, groupby, has.geometry, order, fields)
  if (!is.null(result) && is.list(result)) {
    result.df <- .query2df(obj, result, has.geometry = src.has.geometry)
    if(!is.null(result.df)){
      if(add.smx){
        result.df@data["SMX"] <- NULL
      }
      if(add.smx){
        result.df@data["SMY"] <- NULL
      }
    }
    return(result.df)
  } else{
    return(NULL)
  }
}

.QueryWithBounds <- function(obj, bounds, filter="", order=""){
  if (is.null(obj)) {
    stop("obj must be valid")
  }
  if (class(obj) != "DatasetVector") {
    stop("obj must be DatasetVector")
  }
  if (!.IsDatasetTypeValid(GetDatasetType(obj))) {
    stop()
  }

  qbounds <- c()
  if (is.null(bounds)) {
    dtbounds <- GetBounds(obj)
    qbounds <- c(dtbounds[1,],dtbounds[2,])
  } else if (is.numeric(bounds)) {
    qbounds <- bounds
  } else if(is.matrix(bounds)) {
    qbounds <- c(bounds[1,],bounds[2,])
  } else if( is.list(bounds)) {
    qbounds <- c(bounds[[1]],bounds[[3]],bounds[[3]],bounds[[4]])
  } else {
    stop("invalid bounds, bounds can be a list with (left,top,right,bottom) or matrix.")
  }
  res <- .iCall("R_DatasetVector_query_bounds", obj@pointer, qbounds, filter, order, PACKAGE="RSuperMap")
  if (is.null(res) || is.logical(res)) {
    warning("query failed")
    return(NULL)
  } else{
    return(res)
  }
}
QueryWithBounds <- function(obj, bounds, filter="", order=""){
  result <- .QueryWithBounds(obj, bounds, filter, order)
  if (!is.null(result) && is.list(result)) {
    return(.query.result.fields2df(obj, result = result))
  } else {
    return(NULL)
  }
}

QueryWithBounds2DF <- function(obj, bounds, filter="", order=""){
  result <- .QueryWithBounds(obj, bounds, filter, order)
  if (!is.null(result) && is.list(result)) {
    .query2df(obj, result)
  } else {
    return(NULL)
  }
}

.QueryWithGeometry <- function(obj, geo, filter="", order="", mode=""){
  if (is.null(obj)) {
    stop("obj must be valid")
  }
  if (class(obj) != "DatasetVector") {
    stop("obj must be DatasetVector")
  }
  if (!.IsDatasetTypeValid(GetDatasetType(obj))) {
    stop()
  }
  
  if(is.null(mode) || !is.character(mode)){
    stop("mode must be valid caharcter")  
  }
  if(!.IsSpatialQueryModeValid(mode)){
    stop()
  }
  if(is.null(geo)){
    stop("geo must be valid")
  }
  
  if(!.IsGeoTypeValid(GetType(geo))){
    stop("invalid geometry type")
  }
  res <- .iCall("R_DatasetVector_query_geometry", obj@pointer, geo@pointer, filter, order, mode, PACKAGE="RSuperMap")
  if(is.null(res) || is.logical(res)){
    warning("query failed")
    return(NULL)
  } else {
    return(res)
  }
}

QueryWithGeometry <- function(obj, geo, filter="", order="", mode=""){
  result <- .QueryWithGeometry(obj, geo, filter, order, mode)
  if (!is.null(result) && is.list(result)) {
    return(.query.result.fields2df(obj, result = result))
  } else{
    return(NULL)
  }
}

QueryWithGeometry2DF <- function(obj, geo, filter="", order="", mode=""){
  result <- .QueryWithGeometry(obj, geo, filter, order, mode)
  if (!is.null(result) && is.list(result)) {
    return(.query2df(obj, result))
  } else{
    return(NULL)
  }
}